package com.greatwqs.wmall.common.module.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;

/***
 * Jackson: ObjectMapper 基础配置
 * <a href="https://gitee.com/greatwqs/wmall-be/issues/IBX1ES">ObjectMapper</a>
 *
 * @author wang-qingsong
 * @since 2025/03/29
 */
public class ObjectMapperUtil {

    private static final String DEFAULT_TIME_ZONE = "Asia/Shanghai";
    private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    private static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public static ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();

        // ==================== 1. 基础配置 ====================
        // 禁用空对象序列化时的失败行为（空对象将序列化为{}而不是抛出异常）
        // 使用场景：当序列化没有属性的对象时避免报错
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        // 禁用未知属性反序列化时的失败行为
        // 使用场景：API版本兼容，允许JSON中包含对象中没有的属性
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // ==================== 2. null值处理策略 ====================
        // 设置序列化时包含所有属性，即使为null
        // 使用场景：需要保持JSON结构完整性的场景
        // 替代方案：JsonInclude.Include.NON_NULL（不序列化null值）
        //          JsonInclude.Include.NON_EMPTY（不序列化null和空集合）
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);

        // ==================== 3. 日期时间处理 ====================
        // 禁用将日期序列化为时间戳（使用格式化字符串代替）
        // 使用场景：需要人类可读的日期格式
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // 禁用反序列化时自动调整日期到当前时区
        // 使用场景：保持原始时区信息，不自动转换
        mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

        // 传统java.util.Date配置
        // 设置默认时区为亚洲/上海（GMT+8）
        TimeZone timeZone = TimeZone.getTimeZone(DEFAULT_TIME_ZONE);
        mapper.setTimeZone(timeZone);

        // 设置Date类型的格式化样式
        // 使用场景：统一传统Date类型的输出格式
        SimpleDateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
        dateFormat.setTimeZone(timeZone);
        mapper.setDateFormat(dateFormat);

        // ==================== 4. Java 8日期时间支持 ====================
        JavaTimeModule timeModule = new JavaTimeModule();

        // LocalDateTime配置（带时间的完整日期）
        // 使用场景：需要精确到秒的时间记录，如订单创建时间
        DateTimeFormatter localDateTimeFormat = DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT);
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(localDateTimeFormat));
        timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(localDateTimeFormat));

        // LocalDate配置（仅日期部分）
        // 使用场景：只需要日期的场景，如生日、有效期等
        DateTimeFormatter localDateFormat = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
        timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(localDateFormat));
        timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(localDateFormat));

        // ==================== 5. 注册所有模块 ====================
        // 注册Java时间模块（支持Java 8日期时间API）
        // 注册参数名模块（支持通过构造函数参数名反序列化）
        // 注册JDK8模块（支持Optional等JDK8特性）
        mapper.registerModules(
                timeModule,
                new ParameterNamesModule(),
                new Jdk8Module()
        );

        return mapper;
    }
}
